// Copyright (c) 2020 cgsdfc
// 
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT

// Generated file. Don't edit!
#pragma once
// clang-format off
#include "pyast/pybind_defs.h"
#include "pyast/undefs.h"
// clang-format on

#include "pyast/ast_node.h" // AstNode
#include "pyast/arena.h"
#include "pyast/asdl_seq.h"
#include "pyast/logging.h"
#include "pyast/location.h"

<%!
# Code shared by ast.cc/.h will goes into this module.
from astlib.cc_types import (
    ProductNode, AstNode, Location, TypeNode, ConstructorNode,
    FinalAstNode, Object, AsdlSeq)
%>

namespace pyast {

## Forward declarations
%for cls in hierarchy.class_list:
class ${cls.name};
%endfor

## Enums
%for e in hierarchy.enum_list:
    enum class ${e.name} : int {
        %for m in e.members:
            ${m.name} = ::${m.value},
        %endfor
    };

%endfor

## Classes
%for cls in hierarchy.class_list:
class ${cls.name} : public ${cls.parent} {
public:
    ${cls.name}() = default;
    ${cls.name}(const ${cls.name}&) = default;
    ${cls.name}& operator=(const ${cls.name}&) = default;

%if isinstance(cls, (TypeNode, ProductNode)):

    using underlying_type = ${cls.underlying_type};

    ${cls.name}(underlying_type ptr, Arena* arena) : ${cls.parent}(ptr, arena) {}

    underlying_type ${cls.GetRawPtr()} const {
        return reinterpret_cast<underlying_type>(${cls.GetUntypedRawPtr()});
    }

%endif

%if isinstance(cls, TypeNode):

    ## A member typedef for the KindEnum for its subclass.
    using ${cls.ENUM_TYPE} = ${cls.enum_type};

    ## Get the kind of subclass.
    ${cls.ENUM_TYPE} ${cls.GET_KIND}() const {
        return static_cast<${cls.ENUM_TYPE}>(ptr()->kind);
    }

%endif

%if isinstance(cls, FinalAstNode):

    %if len(cls.members):
        ${cls.name}(${cls.formal_args}, Arena* arena);
    %else:
         explicit ${cls.name}(Arena* arena);
    %endif

    %for m in cls.members:

        ${m.typename} ${m.getter_name}() const;
        void ${m.setter_name}(${m.typename} value);

    %endfor
%endif

%if isinstance(cls, Location):

    %if cls.has_location:
        Location GetStart() const {
            return ${cls.LocationGetter(Location.LOCATION_FIELDS)};
        }

        void SetStart(const Location& loc) {
            ${cls.LocationSetter(Location.LOCATION_FIELDS, 'loc')}
        }
    %endif

    %if cls.has_end_location:
        Location GetEnd() const {
            return ${cls.LocationGetter(Location.LOCATION_END_FIELDS)};
        }

        void SetEnd(const Location& loc) {
            ${cls.LocationSetter(Location.LOCATION_END_FIELDS, 'loc')}
        }
    %endif

%endif

%if isinstance(cls, ConstructorNode):
    static constexpr auto ${cls.ENUM_VALUE} = ${TypeNode.ENUM_TYPE}::${cls.enum_value};
%endif

};

%endfor

%for cls in hierarchy.class_list:
    %if isinstance(cls, FinalAstNode):
        %for m in cls.members:

inline ${m.typename} ${cls.name}::${m.getter_name}() const {
    return ${cls.MemberGetter(m)};
}

inline void ${cls.name}::${m.setter_name}(${m.typename} value) {
    %if m.is_required:
        PYAST_INTERNAL_DCHECK(value, "field ${m.name} of ${cls.name} is required.");
    %endif
    %if isinstance(m.type, AstNode):
        PYAST_INTERNAL_DCHECK(!value || value.${AstNode.GetArena()} == ${cls.GetArena()},
            "Arena of the new value to the field must match that of the node");
    %endif
    %if isinstance(m.type, Object):
        %if not m.is_required:
            if (value)
        %endif
        ${cls.GetArena()}->AddObject(value);
    %endif
    ${cls.MemberSetter(m, 'value')};
}

        %endfor
    %endif
%endfor


} // namespace pyast

